package cn.tedu.csmall.passportbackup.filter;

import cn.tedu.csmall.passportbackup.security.LoginPrincipal;
import cn.tedu.csmall.passportbackup.web.JsonResult;
import cn.tedu.csmall.passportbackup.web.ServiceCode;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

@Slf4j
@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {

    public JwtAuthorizationFilter() {
        log.debug("创建过滤器：JwtAuthorizationFilter");
    }

    @Value("${csmall.jwt.secrte-key}")
    private String secrteKey;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        log.debug("执行JwtAuthorizationFilter过滤器");
        //每次执行过滤器之前都要清空一下
        SecurityContextHolder.clearContext();

        //从请求头当中获取jwt
        String jwt = request.getHeader("Authorization");
        log.debug("从请求头当中获取的jwt:{}",jwt);

        //检查请求是否携带jwt
        if(!StringUtils.hasText(jwt)){
            log.debug("请求当中没有携带jwt");
            //将没有jwt的请求进行放行
            filterChain.doFilter(request,response);
            return;
        }
        // 设置响应结果的文档类型，主要用于处理解析JWT时的异常
        response.setContentType("application/json; charset=utf-8");
        //尝试解析jwt
        log.debug("准备生成jwt，secretkey:{}",secrteKey);
        Claims claims = null;
        try {
            claims = Jwts.parser().setSigningKey(secrteKey).parseClaimsJws(jwt).getBody();
        }catch (MalformedJwtException e){
            log.warn("解析JWT失败：{}：{}",e.getClass().getName(),e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_PARSE, "无法获取有效的登录信息，请重新登录");
            String toJSONString = JSON.toJSONString(jsonResult);
            PrintWriter writer = response.getWriter();
            writer.println(toJSONString);
            writer.close();
            return;
        }catch (SignatureException e){
            log.warn("解析JWT失败：{}：{}",e.getClass().getName(),e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_PARSE, "无法获取有效的登录信息，请重新登录");
            String toJSONString = JSON.toJSONString(jsonResult);
            PrintWriter writer = response.getWriter();
            writer.println(toJSONString);
            writer.close();
            return;
        }catch (ExpiredJwtException e){
            log.warn("解析JWT失败：{}：{}",e.getClass().getName(),e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_EXPIRED, "登录信息已过期，请重新登录");
            String toJSONString = JSON.toJSONString(jsonResult);
            PrintWriter writer = response.getWriter();
            writer.println(toJSONString);
            writer.close();
            return;
        }catch (Throwable e){
            log.warn("解析JWT失败：{}：{}",e.getClass().getName(),e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_PARSE, "无法获取有效的登录信息，请重新登录");
            String toJSONString = JSON.toJSONString(jsonResult);
            PrintWriter writer = response.getWriter();
            writer.println(toJSONString);
            writer.close();
            return;
        }

        //开始获取解析的结果
        Long id = claims.get("id", Long.class);
        log.debug("从JWT中解析得到的id={}",id);
        String username = claims.get("username", String.class);
        log.debug("从JWT中解析得到的username={}",username);
        String authorityListString = claims.get("authorities", String.class);
        log.debug("从JWT中解析得到的authorities={}",authorityListString);

        //准备Authentication对象，后续封装到Security的上下文(如果不封装，则只能存放一个属性)
        LoginPrincipal loginPrincipal = new LoginPrincipal();
        loginPrincipal.setId(id);
        loginPrincipal.setUsername(username);

        //准备权限封装到Authtication当中
        List<SimpleGrantedAuthority> authorities = JSON.parseArray(authorityListString, SimpleGrantedAuthority.class);

        //准备Authentication对象，将从jwt查询到的结果结果封装到Authentication对象，然后再传入Security的上下文
        Authentication authentication = new UsernamePasswordAuthenticationToken(loginPrincipal,null,authorities);

        //将解析出来的结果封装到Security的上下文
        /**
         * 1、先获取Security上下文的对象
         * 2、将信息封装到上下文当中
         */
        SecurityContext securityContext = SecurityContextHolder.getContext();
        securityContext.setAuthentication(authentication);
        log.debug("已经向Security的上下文写入：{}",authentication);

        //对请求选择放行
        filterChain.doFilter(request,response);
    }
}
